//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////


#include "precomp.h"

extern BOOL g_bLastCmdPIN2;

DWORD g_dwCLCKStatus;

//
//
//
static HRESULT ParseGetUserIdentity(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetUserIdentity);
    LPSTR szUserID = NULL;
    HRESULT hr = S_OK;

    szUserID = (LPSTR)AllocBlob(MAXLENGTH_USERID);
    if (!szUserID) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(szUserID, 0x00, MAXLENGTH_USERID);

    // Parse "<prefix><IMSI><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                        ||
        !ParseUnquotedString(szRsp, '\r', szUserID, MAXLENGTH_USERID, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)szUserID;
    cbBlob = MAXLENGTH_USERID;

Error:
    if (FAILED(hr)) {
        FreeBlob(szUserID);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetUserIdentity(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetUserIdentity);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, "AT+CIMI\r", CMDOPT_SUPPRESSLOGGING, APIID_GETUSERIDENTITY, ParseGetUserIdentity, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT ParseGetPhoneLockedState(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetPhoneLockedState);
    UINT i;
    DWORD* pdwState = NULL;
    char szState[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwState = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwState) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwState, 0x00, sizeof(DWORD));
    // Parse "<prefix>+CPIN: <state><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                               ||
        !MatchStringBeginning(szRsp, "+CPIN: ", szRsp)              ||
        !ParseUnquotedString(szRsp, '\r', szState, MAX_ATCMD_LEN, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 1; i <= NUM_LOCKEDSTATES; i++) {
        if (!strcmp(szState, g_rgszLockedStates[i])) {
            *pdwState = i;
            break;
        }
    }
    if (i > NUM_LOCKEDSTATES) {
        RILRetailTrace((TEXT("RilDrv: Unknown SIM state\r\n")));
        // We couldn't match the response with anything
        *pdwState = RIL_LOCKEDSTATE_UNKNOWN;
    }
    else
    {
        // PIN-state PIN2 required? The next AT+CPIN=<xxxx> has to be mapped to AT+CPIN2=<xxxx>.
        if (*pdwState==RIL_LOCKEDSTATE_SIM_PIN2 || *pdwState==RIL_LOCKEDSTATE_SIM_PUK2)
            g_bLastCmdPIN2 = TRUE;
        else
            UpdateSIMState(*pdwState);
    }

    pBlob = (void*)pdwState;
    cbBlob = sizeof(DWORD);
Error:
    if (FAILED(hr)) {
        FreeBlob(pdwState);
    }
    return hr;
}

HRESULT ParseGetPhoneLockedStateFE(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetPhoneLockedState);
    UINT i;
    DWORD* pdwState = NULL;
    char szState[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwState = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwState) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwState, 0x00, sizeof(DWORD));
    // Parse "<prefix>+CPIN: <state><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                               ||
        !MatchStringBeginning(szRsp, "+CPIN: ", szRsp)              ||
        !ParseUnquotedString(szRsp, '\r', szState, MAX_ATCMD_LEN, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 1; i <= NUM_LOCKEDSTATES; i++) {
        if (!strcmp(szState, g_rgszLockedStates[i])) {
            *pdwState = i;
            break;
        }
    }
    if (i > NUM_LOCKEDSTATES) {
        RILRetailTrace((TEXT("RilDrv: Unknown SIM state\r\n")));
        // We couldn't match the response with anything
        *pdwState = RIL_LOCKEDSTATE_UNKNOWN;
    }
    else
    {
        UpdateSIMState(*pdwState);
    }

    // Special case: we have to treat PH-NET PUK in the responce to an
    // AT+CPIN=...;+CPIN? command as an error. Since the Siemens modules
    // doesn't make a distinction between PH-NET PIN and PH-NET PUK we
    // always report PH-NET PIN is missang as the error code.
    if (*pdwState == RIL_LOCKEDSTATE_PH_NET_PUK || *pdwState == RIL_LOCKEDSTATE_PH_NET_PIN)
    {
        hr = RIL_E_NETWKPINREQUIRED;
        goto Error;
    }

    pBlob = (void*)pdwState;
    cbBlob = sizeof(DWORD);
Error:
    if (FAILED(hr)) {
        FreeBlob(pdwState);
    }
    return hr;
}

//
//
//
HRESULT ParseGetPhoneLockedState2(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetPhoneLockedState2);
    UINT i;
    DWORD* pdwState = NULL;
    char szState[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwState = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwState) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwState, 0x00, sizeof(DWORD));
    // Parse "<prefix>+CPIN: <state><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                               ||
        !MatchStringBeginning(szRsp, "+CPIN2: ", szRsp)              ||
        !ParseUnquotedString(szRsp, '\r', szState, MAX_ATCMD_LEN, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 1; i <= NUM_LOCKEDSTATES; i++) {
        if (!strcmp(szState, g_rgszLockedStates[i])) {
            *pdwState = i;
            break;
        }
    }
    if (i > NUM_LOCKEDSTATES) {
        RILRetailTrace((TEXT("RilDrv: Unknown SIM state\r\n")));
        // We couldn't match the response with anything
        *pdwState = RIL_LOCKEDSTATE_UNKNOWN;
    }

    pBlob = (void*)pdwState;
    cbBlob = sizeof(DWORD);
Error:
    if (FAILED(hr)) {
        FreeBlob(pdwState);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetPhoneLockedState(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetPhoneLockedState);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (g_bLastCmdPIN2) {
        if (!QueueCmd(COMMAND_PORT, pHandle, "AT+CPIN2?\r", CMDOPT_NONE|CMDOPT_CPIN2RELATED, APIID_GETPHONELOCKEDSTATE, ParseGetPhoneLockedState2, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (!QueueCmd(COMMAND_PORT, pHandle, "AT+CPIN?\r", CMDOPT_NONE, APIID_GETPHONELOCKEDSTATE, ParseGetPhoneLockedState, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_UnlockPhone(DWORD dwParam, LPCSTR lpszPassword, LPCSTR lpszNewPassword)
{
    TBD_FUNCTION(RILDrv_UnlockPhone);
    TBD_ASSERT(NULL != lpszPassword);

    char szCmd[SECUR_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef RIL_USE_CPIN_WORKAROUND
    if (lpszNewPassword) {
        BOOL bPIN2;

        bPIN2 = g_bLastCmdPIN2;
        if (bPIN2)
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "ATD**052*%s", lpszPassword);
        else
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "ATD**05*%s", lpszPassword);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "*%s", lpszNewPassword);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "*%s", lpszNewPassword);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "#;", lpszNewPassword);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
        (void)strncpyz(szWalk, "\r", SECUR_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30

        if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_UNLOCKING | CMDOPT_SUPPRESSLOGGING | (bPIN2?CMDOPT_CPIN2RELATED:0), APIID_UNLOCKPHONE, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
#endif
    {
        BOOL bPIN2;

        bPIN2 = g_bLastCmdPIN2;
        if (bPIN2)
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPIN2=\"%s\"", lpszPassword);
        else
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPIN=\"%s\"", lpszPassword);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        if (lpszNewPassword) {
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), ",\"%s\"", lpszNewPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
        if (bPIN2) {
            (void)strncpyz(szWalk, "\r", SECUR_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30

            if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_UNLOCKING | CMDOPT_SUPPRESSLOGGING | CMDOPT_CPIN2RELATED, APIID_UNLOCKPHONE, NULL, NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
        else {
            // We have to add the +CPIN? to be able to get the new SIM state after entering the PIN
            // This is neede in case of an netlock, sind this is only visible after entering the PIN.
            // Currently only the MMI of the SmartPhone Edition is able to display a netlock unlock
            // dialog.
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), ";+CPIN?", lpszNewPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
            (void)strncpyz(szWalk, "\r", SECUR_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30

            if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_UNLOCKING | CMDOPT_SUPPRESSLOGGING, APIID_UNLOCKPHONE, ParseGetPhoneLockedStateFE, NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetLockingStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetLockingStatus);
    UINT nValue;
    DWORD* pdwStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwStatus, 0x00, sizeof(DWORD));

    // Parse "<prefix>+CLCK: <status><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                           ||
        !MatchStringBeginning(szRsp, "+CLCK: ", szRsp)          ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwStatus = (nValue ? RIL_LOCKINGSTATUS_ENABLED : RIL_LOCKINGSTATUS_DISABLED);

    pBlob = (void*)pdwStatus;
    cbBlob = sizeof(DWORD);

    g_dwCLCKStatus = nValue;

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwStatus);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetLockingStatus(DWORD dwParam, DWORD dwFacility, LPCSTR lpszPassword)
{
    TBD_FUNCTION(RILDrv_GetLockingStatus);
    TBD_ASSERT(dwFacility && NUM_LOCKFACILITIES > dwFacility);

    char szCmd[SECUR_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (lpszPassword) {
        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CLCK=\"%s\",2,\"%s\"\r",
                         g_rgszLockFacilities[dwFacility], lpszPassword);
    } else {
        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CLCK=\"%s\",2\r",
                         g_rgszLockFacilities[dwFacility]);
    }
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING|(dwFacility==RIL_LOCKFACILITY_SIM_FIXEDIALING?CMDOPT_CPIN2RELATED:0), APIID_GETLOCKINGSTATUS, ParseGetLockingStatus, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

const CME_TRANSLATION g_CMET_CPWD[] = {{RIL_E_OPNOTALLOWED,RIL_E_SIMPUK2REQUIRED},{NULL,NULL}};
const CME_TRANSLATION g_CMET_CCLK[] = {{RIL_E_INVALIDINDEX,RIL_E_SIMPIN2REQUIRED},{RIL_E_INCORRECTPASSWORD,RIL_E_SIMPIN2REQUIRED},{NULL,NULL}};
const CME_TRANSLATION g_CMET_CPWDADI[] = {{RIL_E_INCORRECTPASSWORD,RIL_E_OPNOTALLOWED},{NULL,NULL}};

//
//
//
HRESULT RILDrv_SetLockingStatus(DWORD dwParam, DWORD dwFacility, LPCSTR lpszPassword, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_SetLockingStatus);
    TBD_ASSERT(dwFacility && NUM_LOCKFACILITIES > dwFacility);

    char szCmd[SECUR_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    UINT nValue;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (RIL_LOCKINGSTATUS_DISABLED == dwStatus) {
        nValue = 0;
    } else if (RIL_LOCKINGSTATUS_ENABLED == dwStatus) {
        nValue = 1;
    } else {
        TBD_ASSERT(FALSE);
    }

    if (lpszPassword) {
        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CLCK=\"%s\",%u,\"%s\"\r",
                         g_rgszLockFacilities[dwFacility], nValue, lpszPassword);
    } else {
#ifdef RIL_USE_CPIN2_WORKAROUND
        char szPIN2[MAXLEN_PIN2+1];

        if (dwFacility == RIL_LOCKFACILITY_SIM_FIXEDIALING && !IS_ADI && FetchPIN2Secure(szPIN2, MAXLEN_PIN2))
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CLCK=\"%s\",%u, \"%s\"\r", g_rgszLockFacilities[dwFacility], nValue, szPIN2);
        else
#endif
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CLCK=\"%s\",%u\r", g_rgszLockFacilities[dwFacility], nValue);
    }
    if (!QueueCmdCMET(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING|(dwFacility==RIL_LOCKFACILITY_SIM_FIXEDIALING?CMDOPT_CPIN2RELATED:0), APIID_SETLOCKINGSTATUS, NULL, NULL, hr, dwFacility==RIL_LOCKFACILITY_SIM_FIXEDIALING?g_CMET_CCLK:NULL)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_ChangeLockingPassword(DWORD dwParam, DWORD dwFacility, DWORD dwOldPasswordType, LPCSTR lpszOldPassword, LPCSTR lpszNewPassword)
{
    TBD_FUNCTION(RILDrv_ChangeLockingPassword);
    TBD_ASSERT(dwFacility && NUM_LOCKFACILITIES > dwFacility);
    TBD_ASSERT(NULL != lpszOldPassword);
    TBD_ASSERT(NULL != lpszNewPassword);

    char szCmd[SECUR_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }


    if (RIL_PASSWORDTYPE_PIN == dwOldPasswordType)
    {
        BOOL bUseCMETforPIN = FALSE;
        const CME_TRANSLATION *pCMET;
        
#ifdef RIL_USE_CPIN2_WORKAROUND
        if(RIL_LOCKFACILITY_SIM_PIN2 == dwFacility && !IS_ADI) {
            // Bug in all modules expect MC75: when changing the PIN2 password, the module will
            // still use the old password for the AT+CLCK="FD" command. Unfortunately it is not
            // possible to just reenter the correct PIN2 with AT+CPIN2, so we have to store the
            // PIN2 to pass it to the PIN2 command. We also cannot force the MMI to ask again
            // for the PIN2, since this would conflict with the use of the PIN2 for the fixed
            // dialing phonebook.
            // Side effect: when changing the PIN2 before entering the PIN2, the stored PIN2
            // will be used and the user will not be asked to enter the PIN2. Since it is necessary
            // to enter the old and new PIN2 in order to change the PIN2, this behaviour is
            // acceptable. If we just try to use the AT+CLCK="FD" command, through another bug in
            // the module the PIN2 might become PUK2 blocked.
            if (strlen(lpszNewPassword) <= MAXLEN_PIN2)
                SavePIN2Secure(lpszNewPassword);
        }
#endif
        if (RIL_LOCKFACILITY_SIM == dwFacility && (g_dwModemType == MODEMTYPE_SIEMENS_TC63 || (g_dwModemType == MODEMTYPE_SIEMENS_MC75 && g_dwModemVersion < 2100)))
        {
            g_dwCLCKStatus = MAXDWORD;
            
            // Special workaround for MC75: if the PIN is disabled, the AT+CPWD command returns
            // CME ERROR 16 instead of CME ERROR 3. So in this case we have to translate these errors.
            QueueInternalCmdBlocking(COMMAND_PORT, pHandle->GetDevice(), "AT+CLCK=\"SC\",2\r", CMDOPT_NONE, APIID_GETLOCKINGSTATUS, 0, 0, 0, ParseGetLockingStatus, NULL, NULL, NULL);
            if (g_dwCLCKStatus == 0)
                bUseCMETforPIN = TRUE;
        }
        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPWD=\"%s\",\"%s\",\"%s\"\r",
                         g_rgszLockFacilities[dwFacility], lpszOldPassword, lpszNewPassword);
        
        // Szenario: user tries to change the PIN2, but enters the wrong PIN2 three times, the
        // PUK2 is needed. Unfortunately the Siemens modueles don't return the expected errer
        // message "PUK 2 requiered"
        // Workaround: usage of the CME error translation table to set the right CME error.
        
        if (bUseCMETforPIN)
            pCMET =  g_CMET_CPWDADI;
        else
            pCMET = (dwFacility==RIL_LOCKFACILITY_SIM_PIN2 ? g_CMET_CPWD : NULL);
        
        if (!QueueCmdCMET(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING|((dwFacility==RIL_LOCKFACILITY_SIM_PIN2)||(dwFacility==RIL_LOCKFACILITY_SIM_FIXEDIALING)?CMDOPT_CPIN2RELATED:0), APIID_CHANGELOCKINGPASSWORD, NULL, NULL, hr, pCMET)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (RIL_PASSWORDTYPE_PUK == dwOldPasswordType)
    {
        // The "old" password is the PIN unblocking key that is used to
        // unblock either PIN or PIN2.
        TBD_ASSERT(RIL_LOCKFACILITY_SIM == dwFacility ||
                   RIL_LOCKFACILITY_SIM_PIN2 == dwFacility);

        if(RIL_LOCKFACILITY_SIM_PIN2 == dwFacility) {
#ifdef RIL_USE_CPIN_WORKAROUND
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "ATD**052*%s*%s*%s#;\r", lpszOldPassword, lpszNewPassword, lpszNewPassword);
#else
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPIN2=\"%s\",\"%s\"", lpszOldPassword, lpszNewPassword);
#endif
        } else {
        // Other hardware uses +CPIN and will fail if no PIN request is pending, per GSM 07.07
#ifdef RIL_USE_CPIN_WORKAROUND
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "ATD**05*%s*%s*%s#;\r", lpszOldPassword, lpszNewPassword, lpszNewPassword);
#else
        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPIN=\"%s\",\"%s\"", lpszOldPassword, lpszNewPassword);
#endif
        }
#ifdef RIL_USE_CPIN_WORKAROUND
        if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_CHANGELOCKINGPASSWORD, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
#else
        if(RIL_LOCKFACILITY_SIM_PIN2 == dwFacility) {
            if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_CHANGELOCKINGPASSWORD, NULL, NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
        else {
            // We have to add the +CPIN? to be able to get the new SIM state after entering the PIN
            // This is neede in case of an netlock, since this is only visible after entering the PIN.
            // Currently only the MMI of the SmartPhone Edition is able to display a netlock unlock
            // dialog.
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), ";+CPIN?\r", lpszNewPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
            if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_CHANGELOCKINGPASSWORD, ParseGetPhoneLockedStateFE, NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
#endif
    }
    else
    {
        hr = E_INVALIDARG;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetCallBarringStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCallBarringStatus);
    UINT nValue;
    UINT nDummy;
    DWORD* pdwStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwStatus, 0x00, sizeof(DWORD));

    // Parse "<prefix>+CLCK: <status>,<info_class><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                           ||
        !MatchStringBeginning(szRsp, "+CLCK: ", szRsp)          ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp)                ||
        !ParseUInt(szRsp, TRUE, nDummy, szRsp)                  ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwStatus = (nValue ? RIL_BARRINGSTATUS_ENABLED : RIL_BARRINGSTATUS_DISABLED);

    pBlob = (void*)pdwStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwStatus);
    }
    return hr;
}

//
// Siemens specific: if ATD*# is used instead of AT+CLCK, the responses look different
//
static HRESULT ParseGetCallBarringStatusATD(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCallBarringStatus);
    char szBarringType[MAX_ATCMD_LEN];
    UINT nValue;
    UINT nDummy;
    DWORD* pdwStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwStatus, 0x00, sizeof(DWORD));

    // Parse "<prefix>^SCLCK: <barring type>,<status>,<info_class><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                           ||
        !MatchStringBeginning(szRsp, "^SCLCK: ", szRsp)          ||
        !ParseString(szRsp, szBarringType, MAX_ATCMD_LEN, szRsp)     ||
        !MatchStringBeginning(szRsp, ",", szRsp)                ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp)                ||
        !ParseUInt(szRsp, TRUE, nDummy, szRsp)                  ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwStatus = (nValue ? RIL_BARRINGSTATUS_ENABLED : RIL_BARRINGSTATUS_DISABLED);

    pBlob = (void*)pdwStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwStatus);
    }
    return hr;
}


// Generation of an ATD-*#-Code instead of AT+CLCK (if possible)
// Parameters: see RILDrv_SetCallBarringStatus()
// Return Value:
// TRUE - ATD-string created
// FALSE - No corroponding ATD available
static BOOL RILDrv_GenerateATDString4CLCK(DWORD dwType, DWORD dwInfoClass, LPCSTR lpszPassword, LPSTR lpszATDString, DWORD dwStatus)
{
    BOOL bATDavailable = FALSE;
    UINT uiBS;

    uiBS = RILDrv_FindBS4InfoClass(dwInfoClass);
    if (uiBS > 0)
    {
        UINT i;

        for (i = 0; i < NUM_BARRING_VALUES; i++)
        {
            if (g_rgBarringTypeTable[i].dwBarringType == dwType && g_rgBarringTypeTable[i].dwBarringStatus == dwStatus)
            {
                if (uiBS >= 100)
                    (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*#;\r", g_rgBarringTypeTable[i].szATD, lpszPassword);
                else
                    (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*%d#;\r", g_rgBarringTypeTable[i].szATD, lpszPassword, uiBS);
                bATDavailable = TRUE;
                break;
            }
        }
    }
    return bATDavailable;
}

//
//
//
HRESULT RILDrv_GetCallBarringStatus(DWORD dwParam, DWORD dwType, DWORD dwInfoClass, LPCSTR lpszPassword)
{
    TBD_FUNCTION(RILDrv_GetCallBarringStatus);
    TBD_ASSERT(dwType && NUM_BARRINGTYPES > dwType);

    char szCmd[SECUR_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    UINT nValue = 0;
    UINT i;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Default value: all services
    if (dwInfoClass == RIL_INFOCLASS_NONE)
        dwInfoClass = RIL_INFOCLASS_DEFAULT_CALLBARRING;

    // In order to pass PTCRB/GCF TC 31.8.3.1 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CLCK
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CLCK(dwType, dwInfoClass, "", szCmd, RIL_BARRINGSTATUS_QUERY))
    {
#endif
        for (i = 0 ; i < NUM_INFOCLASSES; i++) {
            if (dwInfoClass & g_rgdwInfoClasses[i]) {
                nValue |= (0x01 << i);
            }
        }

        if (RIL_INFOCLASS_SMS != dwInfoClass && (nValue & 0x8))
        {
            nValue -= 8;
        }

        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CLCK=\"%s\",2", g_rgszBarringTypes[dwType]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        if (lpszPassword) {
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
#ifdef SUPPORT_INFO_CLASS
        // Only need to add a comma if we'll be adding the infoclass next.
        // This is currently not supported by Siemens modules.
        else if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) != nValue) {
            (void) strncpyz(szWalk, ",", SECUR_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }

        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) != nValue) {
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), ",%u", nValue);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
#endif
        (void) strncpyz(szWalk, "\r", SECUR_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30

        if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_GETCALLBARRINGSTATUS, ParseGetCallBarringStatus, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
#ifndef RIL_USE_GSM0707_CMDS
    }
    else
    {
        if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_GETCALLBARRINGSTATUS, ParseGetCallBarringStatusATD, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
#endif

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetCallBarringStatus(DWORD dwParam, DWORD dwType, DWORD dwInfoClass, LPCSTR lpszPassword, DWORD dwStatus)
{
    TBD_FUNCTION(RILDrv_SetCallBarringStatus);
    TBD_ASSERT(dwType && NUM_BARRINGTYPES > dwType);

    char szCmd[SECUR_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    UINT nValue1;
    UINT nValue2 = 0;
    UINT i;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Default value: all services
    if (dwInfoClass == RIL_INFOCLASS_NONE)
        dwInfoClass = RIL_INFOCLASS_DEFAULT_CALLBARRING;

    // In order to pass PTCRB/GCF TC 31.8.3.1 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CLCK
#ifndef RIL_USE_GSM0707_CMDS
    if (!RILDrv_GenerateATDString4CLCK(dwType, dwInfoClass, lpszPassword, szCmd, dwStatus))
    {
#endif

        if (RIL_BARRINGSTATUS_DISABLED == dwStatus) {
            nValue1 = 0;
        } else if (RIL_BARRINGSTATUS_ENABLED == dwStatus) {
            nValue1 = 1;
        } else {
            TBD_ASSERT(FALSE);
        }

        for (i = 0 ; i < NUM_INFOCLASSES; i++) {
            if (dwInfoClass & g_rgdwInfoClasses[i]) {
                nValue2 |= (0x01 << i);
            }
        }

        if (RIL_INFOCLASS_SMS != dwInfoClass && (nValue2 & 0x8))
        {
            nValue2 -= 8;
        }

        (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CLCK=\"%s\",%u", g_rgszBarringTypes[dwType], nValue1);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        if (lpszPassword) {
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
        // Only need to add a comma if we'll be adding the infoclass next
        else if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) != nValue2) {
            (void) strncpyz(szWalk, ",", SECUR_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }

        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) != nValue2) {
            (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), ",%u", nValue2);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }

        (void) strncpyz(szWalk, "\r", SECUR_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
#ifndef RIL_USE_GSM0707_CMDS
    }
#endif
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_SETCALLBARRINGSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_ChangeCallBarringPassword(DWORD dwParam, DWORD dwType, LPCSTR lpszOldPassword, LPCSTR lpszNewPassword)
{
    TBD_FUNCTION(RILDrv_ChangeCallBarringPassword);
    TBD_ASSERT(dwType && NUM_BARRINGTYPES > dwType);
    TBD_ASSERT(NULL != lpszOldPassword);
    TBD_ASSERT(NULL != lpszNewPassword);

    char szCmd[SECUR_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szWalk, SECUR_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPWD=\"%s\",\"%s\",\"%s\"\r",
                     g_rgszBarringTypes[dwType], lpszOldPassword, lpszNewPassword);
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_CHANGECALLBARRINGPASSWORD, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}
